using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using StockholmsStad.Utilities.Exceptions;

namespace StockholmsStad.Data.Test
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class TestUnitMaintenance : TestUnitBase
    {
        // before running each test 
        [TestInitialize()]
        public void MyTestInitialize()
        {
            BeginTransaction();
        }

        // after each test has run
        [TestCleanup()]
        public void MyTestCleanup()
        {
            EndTransaction();
        }

        [TestMethod]
        public void AddAndRetrieveANewTimeBlockWithoutMinisters()
        {
            var timeBlock = new MinisterTimeBlock
                                {
                                    Interval = 5,
                                    StartDateTime = DateTime.Now,
                                    TimeBlockStatus = TimeBlockStatus.Bookable
                                };

            var agent = AgentFactory.GetMaintenanceAgent();
            var origalNumber = agent.GetTimeBlocks(context).Count;
            int timeBlockId = agent.AddTimeBlock(context, timeBlock);
            timeBlock.TimeBlockId = timeBlockId;
            var retreivedNumber = agent.GetTimeBlocks(context).Count;

            origalNumber++;
            Assert.AreNotEqual(0, timeBlockId, "Förväntat id stämmer inte överens med erhållet id");
            Assert.AreEqual(origalNumber, retreivedNumber, string.Format("Antalet tidsblock {0} är inte det förväntade antalet {1}", origalNumber, retreivedNumber));
        }

        [TestMethod]
        public void AddAndRetrieveANewTimeBlockWithMinisters()
        {
            var timeBlock = new MinisterTimeBlock
            {
                Interval = 5,
                StartDateTime = DateTime.Now,
                TimeBlockStatus = TimeBlockStatus.Bookable
            };

            var ministerAgent = AgentFactory.GetMinisterAgent();
            var ministerId = ministerAgent.AddMinister(context, TestEntity.GetNewMinister());

            timeBlock.Minister = new Minister { MinisterId = ministerId };
            timeBlock.TempMinister = new Minister { MinisterId = ministerId };

            var agent = AgentFactory.GetMaintenanceAgent();
            var origalNumber = agent.GetTimeBlocks(context).Count;

            int timeBlockId = agent.AddTimeBlock(context, timeBlock);
            timeBlock.TimeBlockId = timeBlockId;

            var retreivedNumber = agent.GetTimeBlocks(context).Count;

            origalNumber++;
            Assert.AreNotEqual(0, timeBlockId, "Förväntat id stämmer inte överens med erhållet id");
            Assert.AreEqual(origalNumber, retreivedNumber, string.Format("Antalet tidsblock {0} är inte det förväntade antalet {1}", origalNumber, retreivedNumber));
        }

        [TestMethod]
        [ExpectedException(typeof(StockholmStadSystemException))]
        public void AddExistingTimeblock()
        {
            var timeBlock = new MinisterTimeBlock
                                {
                                    Interval = 5,
                                    StartDateTime = DateTime.Now,
                                    TimeBlockStatus = TimeBlockStatus.Bookable
                                };

            var agent = AgentFactory.GetMaintenanceAgent();
            int timeBlockId = agent.AddTimeBlock(context, timeBlock);

            int timeBlockId2 = agent.AddTimeBlock(context, timeBlock);
        }


        [TestMethod]
        public void AddTimeblocksAndGetOneTimeBlockForSelectedDate()
        {
            // lägger på högt datum för att vara säker på att inga sådana datum finns sen tidigare
            var timeBlock1 = new MinisterTimeBlock
            {
                Interval = 5,
                StartDateTime = new DateTime(2050, 12,1),
                TimeBlockStatus = TimeBlockStatus.Bookable
            };

            var timeBlock2 = new MinisterTimeBlock
            {
                Interval = 5,
                StartDateTime = new DateTime(2050, 12, 2),
                TimeBlockStatus = TimeBlockStatus.Bookable
            };

            var timeBlock3 = new MinisterTimeBlock
            {
                Interval = 5,
                StartDateTime = new DateTime(2050, 12, 3),
                TimeBlockStatus = TimeBlockStatus.Bookable
            };

            var agent = AgentFactory.GetMaintenanceAgent();
            int timeBlockId1 = agent.AddTimeBlock(context, timeBlock1);
            int timeBlockId2 = agent.AddTimeBlock(context, timeBlock2);
            int timeBlockId3 = agent.AddTimeBlock(context, timeBlock3);

            timeBlock1.TimeBlockId = timeBlockId1;
            timeBlock2.TimeBlockId = timeBlockId2;
            timeBlock3.TimeBlockId = timeBlockId3;

            var searchDate = new DateTime(2050, 12, 2);
            var timeBlocks = agent.GetTimeBlocks(context, searchDate);
            
            var expectedNoOfBlocks = 1;
            Assert.AreEqual(expectedNoOfBlocks, timeBlocks.Count);

            foreach(var timeBlock in timeBlocks)
                AreTimeBlockEqual(timeBlock2, timeBlock);
        }
        
        [TestMethod]
        public void AddAndRetrieveText()
        {
            var agent = AgentFactory.GetMaintenanceAgent();

            var textTypes = agent.GetTextTypes(context);

            Assert.IsNotNull(textTypes, "Fanns inga texttyper att hämta");
            var textType = textTypes[0];

            var expectedText = new InformationText
                                   {
                                       Content = "Detta är en text",
                                       Header = "En liten rubrik",
                                       TextType = textType,
                                   };

            int id = agent.AddText(context, expectedText);
            Assert.AreNotEqual(0, id, "Texten lades inte till");
            expectedText.TextId = id;

            var actualText = agent.GetText(context, id);

            AreTextsEqual(expectedText, actualText);

        }

        [TestMethod]
        public void AddAndRetrieveSeveralTexts()
        {
            var agent = AgentFactory.GetMaintenanceAgent();

            var textTypes = agent.GetTextTypes(context);
            Assert.IsNotNull(textTypes, "Fanns inga texttyper att hämta");
            var textType1 = textTypes[0];
            var textType2 = textTypes[1];

            var text1 = new InformationText
                            {
                                Content = "Detta är en text",
                                Header = "En liten rubrik",
                                TextType = textType1
                            };

            var text2 = new InformationText
                            {
                                Content = "Detta är en liten till text",
                                Header = "Ännu en rubrik",
                                TextType = textType2
                            };

            int id1 = agent.AddText(context, text1);
            Assert.AreNotEqual(0, id1, "Texten lades inte till");
            text1.TextId = id1;

            var id2 = agent.AddText(context, text2);
            Assert.AreNotEqual(0, id2, "Texten lades inte till");
            text2.TextId = id2;

            Assert.AreNotEqual(id1, id2, "Det skapades en ny text som fick samma id... hmmm....");

            var actualText1 = agent.GetText(context, text1.TextId.Value);
            var actualText2 = agent.GetText(context, text2.TextId.Value);

            AreTextsEqual(text1, actualText1);
            AreTextsEqual(text2, actualText2);
        }

        [TestMethod]
        public void AddAndRetrieveNewReportDefintionWithNewLanguageForWeddingCertificate()
        {
            var agent = AgentFactory.GetMaintenanceAgent();
            var origLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            const string languageName = "nyttspråk";

            var languageAgent = AgentFactory.GetLanguageAgent();
            var newLangId = languageAgent.AddLanguage(context, languageName);

            Assert.AreNotEqual(0, newLangId, "Inget nytt språk lades till");

            var reportDef = TestEntity.GetReportDefinition();

            var newCert = new MarriageCertificate
                              {
                                  Language = new ValueIdPair {Id = newLangId, Name = languageName},
                                  MarriageCertificateType = MarriageCertificateType.Wedding,
                                  ReportDefinition = reportDef,
                              };

            var certId = agent.AddMarriageCertificate(context, newCert);

            Assert.AreNotEqual(0, certId, "Inget ny rapportdef skapades");
            newCert.MarriageCertificateId = certId;
            
            var retrievedCert = agent.GetMarriageCertificate(context, newCert.MarriageCertificateType, newLangId);
            newCert.ReportDefinition.ReportDefinitionId = retrievedCert.ReportDefinition.ReportDefinitionId;

            AreReportCertificatesEqual(newCert, retrievedCert);
            var updatedLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            Assert.AreNotEqual(0, updatedLangs.Count);
            Assert.AreNotEqual(origLangs.Count, updatedLangs.Count);
        }

        [TestMethod]
        public void AddAndRetrieveNewReportDefintionWithNewLanguageAndNullValuesForWeddingCertificate()
        {
            var agent = AgentFactory.GetMaintenanceAgent();
            var origLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            const string languageName = "nyttspråk";

            var languageAgent = AgentFactory.GetLanguageAgent();
            var newLangId = languageAgent.AddLanguage(context, languageName);

            Assert.AreNotEqual(0, newLangId, "Inget nytt språk lades till");

            var reportDef = TestEntity.GetReportDefinitionWithSomeNullValues();

            var newCert = new MarriageCertificate
            {
                Language = new ValueIdPair { Id = newLangId, Name = languageName },
                MarriageCertificateType = MarriageCertificateType.Wedding,
                ReportDefinition = reportDef,
            };

            var certId = agent.AddMarriageCertificate(context, newCert);

            Assert.AreNotEqual(0, certId, "Inget ny rapportdef skapades");
            newCert.MarriageCertificateId = certId;

            var retrievedCert = agent.GetMarriageCertificate(context, newCert.MarriageCertificateType, newLangId);
            newCert.ReportDefinition.ReportDefinitionId = retrievedCert.ReportDefinition.ReportDefinitionId;

            AreReportCertificatesEqual(newCert, retrievedCert);
            var updatedLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            Assert.AreNotEqual(0, updatedLangs.Count);
            Assert.AreNotEqual(origLangs.Count, updatedLangs.Count);
        }

        [TestMethod]
        public void AddRetrieveAndUpdateNewReportDefintionWithNewLanguageForWeddingCertificate()
        {
            var agent = AgentFactory.GetMaintenanceAgent();
            var origLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            const string languageName = "nyttspråk";

            var languageAgent = AgentFactory.GetLanguageAgent();
            var newLangId = languageAgent.AddLanguage(context, languageName);

            Assert.AreNotEqual(0, newLangId, "Inget nytt språk lades till");

            var reportDef = TestEntity.GetReportDefinition();

            var newCert = new MarriageCertificate
            {
                Language = new ValueIdPair { Id = newLangId, Name = languageName },
                MarriageCertificateType = MarriageCertificateType.Wedding,
                ReportDefinition = reportDef,
            };

            var certId = agent.AddMarriageCertificate(context, newCert);

            Assert.AreNotEqual(0, certId, "Inget ny rapportdef skapades");
            newCert.MarriageCertificateId = certId;

            var retrievedCert = agent.GetMarriageCertificate(context, newCert.MarriageCertificateType, newLangId);
            newCert.ReportDefinition.ReportDefinitionId = retrievedCert.ReportDefinition.ReportDefinitionId;

            AreReportCertificatesEqual(newCert, retrievedCert);

            const string newAboveDate = "newAboveDate";
            const string newBelowDate = "newBelowDate";

            reportDef.AboveDate = newAboveDate;
            reportDef.BelowDate = newBelowDate;
            newCert.ReportDefinition = reportDef;
            newCert.MarriageCertificateType = MarriageCertificateType.Wedding;

            agent.UpdateMarriageCertificate(context, newCert);

            var retrievedUpdatedCert = agent.GetMarriageCertificate(context, newCert.MarriageCertificateType, newLangId);
            AreReportCertificatesEqual(newCert, retrievedUpdatedCert);

            var updatedLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            Assert.AreNotEqual(0, updatedLangs.Count);
            Assert.AreNotEqual(origLangs.Count, updatedLangs.Count);
        }

        [TestMethod]
        public void AddRetrieveAndUpdateNewReportDefintionWithNewLanguageAndNullValuesForWeddingCertificate()
        {
            var agent = AgentFactory.GetMaintenanceAgent();
            var origLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            const string languageName = "nyttspråk";

            var languageAgent = AgentFactory.GetLanguageAgent();
            var newLangId = languageAgent.AddLanguage(context, languageName);

            Assert.AreNotEqual(0, newLangId, "Inget nytt språk lades till");

            var reportDef = TestEntity.GetReportDefinitionWithSomeNullValues();

            var newCert = new MarriageCertificate
            {
                Language = new ValueIdPair { Id = newLangId, Name = languageName },
                MarriageCertificateType = MarriageCertificateType.Wedding,
                ReportDefinition = reportDef,
            };

            var certId = agent.AddMarriageCertificate(context, newCert);

            Assert.AreNotEqual(0, certId, "Inget ny rapportdef skapades");
            newCert.MarriageCertificateId = certId;

            var retrievedCert = agent.GetMarriageCertificate(context, newCert.MarriageCertificateType, newLangId);
            newCert.ReportDefinition.ReportDefinitionId = retrievedCert.ReportDefinition.ReportDefinitionId;

            AreReportCertificatesEqual(newCert, retrievedCert);

            const string newAboveDate = "newAboveDate";
            const string newBelowDate = "newBelowDate";

            reportDef.AboveDate = newAboveDate;
            reportDef.BelowDate = newBelowDate;
            newCert.ReportDefinition = reportDef;
            newCert.MarriageCertificateType = MarriageCertificateType.Wedding;

            agent.UpdateMarriageCertificate(context, newCert);

            var retrievedUpdatedCert = agent.GetMarriageCertificate(context, newCert.MarriageCertificateType, newLangId);
            AreReportCertificatesEqual(newCert, retrievedUpdatedCert);

            var updatedLangs = agent.GetLanguagesForCertificateType(context, MarriageCertificateType.Wedding);
            Assert.AreNotEqual(0, updatedLangs.Count);
            Assert.AreNotEqual(origLangs.Count, updatedLangs.Count);
        }

        private static void AreReportCertificatesEqual(MarriageCertificate expected, MarriageCertificate actual)
        {
            Assert.AreEqual(expected.Language.Id, expected.Language.Id, "Språk id stämmer inte överens");
            Assert.AreEqual(expected.Language.Name, expected.Language.Name, "Språkets namn stämmer inte överens");
            Assert.AreEqual(expected.MarriageCertificateId, expected.MarriageCertificateId, "Cert id stämmer inte överens");
            Assert.AreEqual(expected.MarriageCertificateType, expected.MarriageCertificateType, "Cert typ stämmer inte överens");

            AreReportDefEqual(expected.ReportDefinition, actual.ReportDefinition);
        }

        private static void AreReportDefEqual(ReportDefinition expected, ReportDefinition actual)
        {
            Assert.AreEqual(expected.AboveDate, actual.AboveDate,
                string.Format("förväntat AboveDate {0} matchar inte erhållet {1}", expected.AboveDate, actual.AboveDate));
            Assert.AreEqual(expected.BelowDate, actual.BelowDate,
                string.Format("förväntat BelowDate {0} matchar inte erhållet {1}", expected.BelowDate, actual.BelowDate));
            Assert.AreEqual(expected.BelowSignature, actual.BelowSignature,
                string.Format("förväntat BelowSignature {0} matchar inte erhållet {1}", expected.BelowSignature, actual.BelowSignature));
            Assert.AreEqual(expected.BetweenNames, actual.BetweenNames,
                string.Format("förväntat BetweenNames {0} matchar inte erhållet {1}", expected.BetweenNames, actual.BetweenNames));
            Assert.AreEqual(expected.City, actual.City,
                string.Format("förväntat City {0} matchar inte erhållet {1}", expected.City, actual.City));
            Assert.AreEqual(expected.Heading, actual.Heading,
                string.Format("förväntat Heading {0} matchar inte erhållet {1}", expected.Heading, actual.Heading));
            Assert.AreEqual(expected.Inception, actual.Inception,
                string.Format("förväntat Inception {0} matchar inte erhållet {1}", expected.Inception, actual.Inception));
            Assert.AreEqual(expected.ReportDefinitionId, actual.ReportDefinitionId,
                string.Format("förväntat ReportDefinitionId {0} matchar inte erhållet {1}", expected.ReportDefinitionId, actual.ReportDefinitionId));
            Assert.AreEqual(expected.SignLine, actual.SignLine,
                string.Format("förväntat SignLine {0} matchar inte erhållet {1}", expected.SignLine, actual.SignLine));
        }

        private static void AreTextsEqual(InformationText expectedText, InformationText actualText)
        {
            Assert.AreEqual(expectedText.TextId, actualText.TextId,
                string.Format("Det förväntade id:t {0} stämmer inte överens med det erhållna: {1}", expectedText.TextId, actualText.TextId));
            Assert.AreEqual(expectedText.Header, actualText.Header,
                string.Format("Det förväntade rubriken {0} stämmer inte överens med det erhållna: {1}", expectedText.Header, actualText.Header));
            Assert.AreEqual(expectedText.Content, actualText.Content,
                string.Format("Det förväntade innehållet {0} stämmer inte överens med det erhållna: {1}", expectedText.Content, actualText.Content));
            Assert.AreEqual(expectedText.TextType.Name, actualText.TextType.Name,
                            string.Format("Det förväntade typnamnet {0} stämmer inte överens med det erhållna: {1}",
                            expectedText.TextType.Name, actualText.TextType.Name));
            Assert.AreEqual(expectedText.TextType.Id, actualText.TextType.Id,
                            string.Format("Det förväntade typid:t {0} stämmer inte överens med det erhållna: {1}",
                            expectedText.TextType.Id, actualText.TextType.Id));
        }

        private static void AreTimeBlockEqual(TimeBlock actual, TimeBlock expected)
        {
            Assert.AreEqual(actual.StartDateTime.ToString("yyyy-MM-dd"), expected.StartDateTime.ToString("yyyy-MM-dd"), 
                string.Format("Förväntad startdatum {0} match inte erhållet {1}", actual.StartDateTime, expected.StartDateTime));

            Assert.AreEqual(actual.TimeBlockId, expected.TimeBlockId,
                string.Format("Förväntat id {0} match inte erhållet {1}", actual.TimeBlockId, expected.TimeBlockId));
        }


    }
}